/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package abricots.entity;

import abricots.MyLog;
import abricots.entity.collision.EntityCollisionManager;
import abricots.entity.command.BasicEntityCommandManager;
import abricots.entity.drawer.EntityDrawer;
import abricots.entity.simulator.BasicEntitySimulator;
import abricots.entity.state.BasicEntityStateManager;
import abricots.entity.state.EntityState;
import abricots.game.GameEnv;
import com.esotericsoftware.minlog.Log;
import java.util.HashSet;
import java.util.Set;
import org.newdawn.slick.Graphics;

/**
 *
 * @author charly
 */
public abstract class BasicEntity implements Entity {
   protected MyLog log = new MyLog(getClass().getSimpleName(), Log.LEVEL_DEBUG);
   protected BasicEntitySimulator simulator;
   protected EntityCollisionManager collisionManager;
   protected BasicEntityCommandManager commandManager;
   protected EntityDrawer drawer;
   protected BasicEntityStateManager stateManager;
   protected GameEnv env;
   protected Set<Integer> childeren;
   protected int id = -1;
   protected int parentId = -1;
   protected int entityTypeId = -1;
   protected boolean validated = false;

   public BasicEntity() {
      childeren = new HashSet<Integer>();
   }

   public void onValidation() {
   }

   @Override
   public void unregister() {
      for (int childId : childeren) {
         Entity e = env.getEntitiesManager().getEntity(childId);
         e.unregister();
      }
      childeren.clear();
      env.getEntitiesManager().removeEntity(id, !validated);
   }

   @Override
   public void simulate(long fromTime, long targetTime, boolean fromValid) {
      log.trace("Simulation asked from:" + fromTime + " to " + targetTime);
      if (stateManager.getEntityState(targetTime) != null) {
         log.trace(" State already present for " + targetTime);
         return;
      }
      long lastValidTime = stateManager.getLastValid();
      EntityState lastState = null;
      if (fromValid && lastValidTime <= fromTime) {
         lastState = stateManager.getLastEntityStateUpTo(lastValidTime);
      } else if (!fromValid) {
         lastState = stateManager.getEntityState(fromTime);
      }
      if (lastState == null) {
         lastState = stateManager.getLastEntityStateUpTo(fromTime);
         if (lastState == null) {
            log.warn("No entity state to start simulation!");
            return;
         }
      }
      EntityState newState = simulator.simulate(lastState, targetTime, commandManager.getCommands()); //TODO pass only useful commands
      log.trace(" New state after simulation : " + newState.toString());
      stateManager.addState(newState);
      if (validated) {
         env.getWorldStateManager().addState(targetTime, id);
      }

      for (int childId : childeren) {
         Entity entity = env.getEntitiesManager().getEntity(childId);
         if (entity == null) {
            log.warn("Unknown entity in childeren: " + childId);
            continue;
         }
         entity.simulate(fromTime, targetTime, fromValid);
      }
   }

   @Override
   public void draw(Graphics g, long targetTime) {
      EntityState state = stateManager.getEntityState(targetTime);
      if (state == null) {
         log.warn("No state found for draw time " + targetTime);
         state = stateManager.getLastEntityStateUpTo(targetTime);
      }
      EntityState drawState = state.getCopy();
      drawer.draw(drawState, g);

      for (int childId : childeren) {
         Entity entity = env.getEntitiesManager().getEntity(childId);
         if (entity == null) {
            log.warn("Unknown entity in childeren: " + childId);
            continue;
         }
         entity.draw(g, targetTime);
      }
   }

   @Override
   public void cleanUp(long lastNeeded, long lastValid) {
      log.trace("cleanup " + lastNeeded + "  " + lastValid);
      long needed = lastNeeded;
      long valid = lastValid;
      // decrease needed to last valid state received from server
      if (stateManager != null && needed >= 0) {
         long lastValidState = stateManager.getLastValid();
         if (lastValidState >= 0 && (needed < 0 || needed > lastValidState)) {
            needed = lastValidState;
         }
      }
      if (needed >= 0) {
         log.trace("clearing before needed " + needed);
         stateManager.clearAllBefore(needed);
         env.getWorldStateManager().clearAllBefore(id, needed);
         commandManager.clearAllBefore(needed);
      }
      if (valid >= 0) {
         log.trace("clearing after valid " + valid);
         stateManager.clearAllAfter(valid);
         env.getWorldStateManager().clearAllAfter(id, valid);
      }

      for (int childId : childeren) {
         Entity entity = env.getEntitiesManager().getEntity(childId);
         if (entity == null) {
            log.warn("Unknown entity in childeren: " + childId);
            continue;
         }
         entity.cleanUp(lastNeeded, lastValid);
      }
   }

   @Override
   public int getId() {
      return id;
   }

   @Override
   public void setId(int id) {
      this.id = id;
   }

   @Override
   public int getEntityTypeId() {
      return entityTypeId;
   }

   @Override
   public void addChild(int childId) {
      Entity entity = env.getEntitiesManager().getEntity(childId);
      if (entity == null) {
         log.warn("Unknown entity to put as child: " + childId);
         return;
      }
      if (entity.getParentId() >= 0) {
         Entity oldParent = env.getEntitiesManager().getEntity(entity.getParentId());
         oldParent.removeChild(childId);
         log.warn("Changing parent of " + entity.toString());
      }
      entity.setParent(this.id);
      childeren.add(id);
   }

   @Override
   public void removeChild(int childId) {
      Entity entity = env.getEntitiesManager().getEntity(childId);
      if (entity == null) {
         log.warn("Unknown entity to remove as child: " + childId);
         return;
      }
      entity.setParent(-1);
      childeren.remove(childId);
   }

   @Override
   public Set<Integer> getChilderen() {
      return childeren;
   }

   @Override
   public int getParentId() {
      return parentId;
   }

   @Override
   public void setParent(int id) {
      parentId = id;
   }

   @Override
   public boolean isValidated() {
      return validated;
   }

   @Override
   public void setValidated(boolean validated) {
      this.validated = validated;
   }

   @Override
   public EntityCollisionManager getCollisionManager() {
      return collisionManager;
   }

   @Override
   public BasicEntityCommandManager getCommandManager() {
      return commandManager;
   }

   @Override
   public EntityDrawer getDrawer() {
      return drawer;
   }

   public GameEnv getEnv() {
      return env;
   }

   @Override
   public BasicEntitySimulator getSimulator() {
      return simulator;
   }

   @Override
   public BasicEntityStateManager getStateManager() {
      return stateManager;
   }
}
